# views.py 视图集
import hashlib
import base64
import json

from django.contrib.auth.models import Group
from django.views.decorators.csrf import csrf_exempt
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet, GenericViewSet
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from rest_framework.pagination import PageNumberPagination
from rest_framework.exceptions import NotFound
from .models import Product, Banner, SpecialOffer, Category, UserInfo, SalesOrder, LogisticsInfo
from .serializers import (ProductSerializer, BannerSerializer, SpecialOfferSerializer,
                          CategorySerializer, CategoryWithProductsSerializer, UserInfoSerializer,
                          SalesOrderSerializer, LogisticsSerializer, UserInfoDetailSerializer, GroupDetailSerializer)

from rest_framework.permissions import IsAuthenticated, IsAdminUser

from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page

from django.core.exceptions import ObjectDoesNotExist

from django.conf import settings
import requests
from rest_framework import status
from utils.wx_crypt import WXBizDataCrypt
import jwt
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import AllowAny
from django.shortcuts import render, redirect
from django.contrib.auth.models import Permission, Group
from utils.get_permission import get_permission_names


def get_user_permissions(user, perms_qs):
    """返回按app和模型分类的权限字典"""
    permissions_dict = {}
    for perm in perms_qs:
        app = perm.content_type.app_label
        model = perm.content_type.model_class().__name__ if perm.content_type.model_class() else perm.content_type.model
        if app not in permissions_dict:
            permissions_dict[app] = {}
        if model not in permissions_dict[app]:
            permissions_dict[app][model] = []
        permissions_dict[app][model].append(perm.codename.replace(f'_{perm.content_type.model}', ''))
    return permissions_dict


def index(request, aid=None):
    # print(f"user: {request.user}, Authenticated: {request.user.is_authenticated}")
    user = request.user
    direct_groups_permissions = get_permission_names(user)
    # print(f"用户自己的所有的直接权限+组权限(中文名): {direct_groups_permissions}")

    groups = user.groups.all()
    groups_name = [group.name for group in groups]
    # print(f"用户所属组: {groups_name}")

    # 获取用户所有权限（组权限 + 直接权限）
    # user_permissions = [group.permissions.values_list('codename', flat=True) for group in groups]
    perms_qs = (
            user.user_permissions.all() |
            Permission.objects.filter(group__in=user.groups.all())
    ).select_related('content_type').distinct()

    user_permissions = get_user_permissions(request.user, perms_qs)  # 调用自定义函数
    # print(f"用户自己的所有的权限(直+组)(英文名): {user_permissions}")

    # 获取项目所有权限
    permissions = Permission.objects.all().order_by('content_type__app_label', 'codename')
    all_permissions = get_user_permissions(request.user, permissions)  # 调用自定义函数
    # print(f"所有的权限(英文名): {all_permissions}")

    if request.method == 'POST':
        pass

    menu_data = [
        {
            'name': '首页' if groups_name or direct_groups_permissions else '您还未授权',
            'has_permission': True,
            'icon': 'fas fa-home',
            'style': 'font-size: clamp(0.7rem, 1.5vw, 0.8rem); color: #1c7899',
            'url': '#',
        },
        {
            'name': '系统设置',
            'has_permission': '系统设置' in direct_groups_permissions,
            'icon': 'bi bi-cpu-fill',
            'style': 'font-size: clamp(0.7rem, 1.5vw, 0.8rem); color: #1c7899',
            'sub_items': [
                {'name': '组权分配',
                 'has_permission': '组权分配' in direct_groups_permissions,
                 'icon': 'bi bi-sliders fs-6 text-success',
                 'url': 'acc/rbac/group_perm/manage/',
                 },
                {'name': '用户管理',
                 'has_permission': '用户管理' in direct_groups_permissions,
                 'icon': 'bi bi-people-fill fs-6 text-success',
                 'url': 'acc/rbac/user/manage/',
                 },
                {'name': '权限管理',
                 'has_permission': '权限管理' in direct_groups_permissions,
                 'icon': 'bi bi-shield-lock fs-6 text-success',
                 'url': 'acc/rbac/permission/',
                 },
            ],
        },
        {
            'name': '电商管理',
            'has_permission': '电商管理' in direct_groups_permissions,
            'icon': 'fas fa-store',
            'style': 'font-size: clamp(0.7rem, 1.5vw, 0.8rem); color: #1c7899',
            'sub_items': [
                {'name': '横幅管理',
                 'has_permission': '横幅管理' in direct_groups_permissions,
                 'icon': 'bi bi-card-heading fs-6 text-success',
                 'url': 'ecom/banners/list/',
                 },
                {'name': '特价管理',
                 'has_permission': '特价管理' in direct_groups_permissions,
                 'icon': 'bi bi-tag-fill fs-6 text-success',
                 # 'url': 'ecom/banners/list/',
                 },
                {'name': '商品管理',
                 'has_permission': '商品管理' in direct_groups_permissions,
                 'icon': 'bi bi-box-seam fs-6 text-success',
                 'url': 'ecom/product/manage/',
                 },
                {'name': '订单管理',
                 'has_permission': '订单管理' in direct_groups_permissions,
                 'icon': 'bi bi-receipt fs-6 text-success',
                 },
                {'name': '分类管理',
                 'has_permission': '分类管理' in direct_groups_permissions,
                 'icon': 'bi bi-diagram-3 fs-6 text-success',
                 },
                {'name': '报表管理',
                 'has_permission': '报表管理' in direct_groups_permissions,
                 'icon': 'bi-bar-chart-line fs-6 text-success',
                 }
            ],
        },
        {
            'name': '实体管理',
            'has_permission': '实体管理' in direct_groups_permissions,
            'icon': 'bi bi-house',
            'style': 'font-size: clamp(0.7rem, 1.5vw, 0.8rem); color: #1c7899',
            'sub_items': [
                {'name': '进货管理',
                 'has_permission': '进货管理' in direct_groups_permissions,
                 'icon': 'bi bi-cart-plus fs-6 text-success'
                 },
                {'name': '销售管理',
                 'has_permission': '销售管理' in direct_groups_permissions,
                 'icon': 'bi-cart-check fs-6 text-success'
                 },
                {'name': '报表管理',
                 'has_permission': '报表管理' in direct_groups_permissions,
                 'icon': 'bi-activity fs-6 text-success'
                 },
                {'name': '参数设置',
                 'has_permission': '参数设置' in direct_groups_permissions,
                 'icon': 'bi bi-gear fs-6 text-success'
                 }
            ],
        }
    ]

    return render(request, 'base.html', {
        'users': user,
        'group': groups,
        'all_permissions': all_permissions,
        'user_permissions': user_permissions,
        'menu_data': menu_data,
        'is_begin': True,
        'is_login': request.user.is_authenticated,
    })


@method_decorator(csrf_exempt, name='dispatch')
class ProductViewSet(ModelViewSet):
    permission_classes = [AllowAny]  # 允许匿名访问
    serializer_class = ProductSerializer
    queryset = Product.objects.all()

    def get_queryset(self):
        """预取关联SKU数据，避免N+1查询问题"""
        return Product.objects.prefetch_related('skus').all()

    def get_serializer_context(self):
        """强制传递request上下文"""
        context = super().get_serializer_context()
        context.update({'request': self.request})
        return context

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        # 分页处理
        page = self.paginate_queryset(queryset)
        # print(f'Request query params:{request.query_params}')  # 打印所有查询参数
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            # 必须通过分页器返回响应
            return self.get_paginated_response(serializer.data)
        # 非分页情况需手动包装
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)  # ✅ 正确


class BannerViewSet(ModelViewSet):
    permission_classes = [AllowAny]  # 允许匿名访问
    serializer_class = BannerSerializer
    # queryset = Banner.objects.all()
    queryset = Banner.objects.none()  # 可选：避免冲突，初始化为空

    def get_queryset(self):
        return Banner.objects.filter(is_active=True).order_by('order', 'id')  # 优先按order排序，其次按id(预防多个order相同)

    def list(self, request, *args, **kwargs):
        request.query_params._mutable = True
        request.query_params['page'] = 1  # 强制第一页
        return super().list(request, *args, **kwargs)

    def get_serializer_context(self):
        """确保序列化器获得request上下文"""
        context = super().get_serializer_context()
        context.update({'request': self.request})
        return context


class SpecialOfferViewSet(ModelViewSet):
    permission_classes = [AllowAny]  # 允许匿名访问
    serializer_class = SpecialOfferSerializer
    queryset = SpecialOffer.objects.all()

    def list(self, request, *args, **kwargs):
        request.query_params._mutable = True
        request.query_params['page'] = 1  # 强制第一页
        return super().list(request, *args, **kwargs)


# ################# 以下为分类 ##########################
class CategoryPagination(PageNumberPagination):
    permission_classes = [AllowAny]  # 允许匿名访问
    page_size = 10
    page_query_param = 'page'
    page_size_query_param = 'page_size'
    max_page_size = 100


class CategoryViewSet(GenericViewSet, ListModelMixin, RetrieveModelMixin):
    permission_classes = [AllowAny]  # 允许匿名访问
    # lookup_field = 'pk'  # 默认值，可省略
    # lookup_url_kwarg = 'pk'  # 确保URL参数名与视图参数一致
    """
    分类接口集
    GET /categories/ - 获取全部分类
    GET /categories/{id}/ - 获取分类详情及分页商品
    """
    queryset = Category.objects.prefetch_related(
        'products'
    ).all()
    pagination_class = CategoryPagination

    def get_serializer_class(self):
        return CategoryWithProductsSerializer if self.action == 'retrieve' else CategorySerializer

    def retrieve(self, request, *pk):
        """分类详情带分页商品"""
        instance = self.get_object()
        serializer = self.get_serializer(instance)

        # 手动处理分页
        paginator = self.paginator
        products = instance.products.filter(is_active=True)
        page = paginator.paginate_queryset(products, request)

        # 将分页器注入序列化器上下文
        serializer.context['paginator'] = paginator
        return Response(serializer.data)


class ProductPagination(PageNumberPagination):
    permission_classes = [AllowAny]  # 允许匿名访问
    page_size = 10
    page_size_query_param = 'page_size'
    max_page_size = 100
    template = None  # 禁用HTML分页控件

    def get_paginated_response(self, data):
        return Response({
            'count': self.page.paginator.count,
            'next': self.get_next_link(),
            'previous': self.get_previous_link(),
            'results': data
        })


class CategoryDetailAPI(APIView):
    permission_classes = [AllowAny]  # 允许匿名访问
    """分类详情及商品分页接口"""

    def get(self, request, pk):
        try:
            # 获取分类对象（带预取优化）
            category = Category.objects.prefetch_related(
                'products'  # 根据关联名称
            ).get(pk=pk)
        except ObjectDoesNotExist:
            raise NotFound(detail="分类不存在")

        # 序列化分类信息
        category_serializer = CategorySerializer(
            category,
            context={'request': request}
        )

        # 获取并分页商品数据
        products = category.products.filter(is_active=True).order_by('-created_at')
        paginator = ProductPagination()
        page = paginator.paginate_queryset(products, request)

        # 序列化商品数据
        product_serializer = ProductSerializer(
            page,
            many=True,
            context={'request': request}
        )

        # 构建响应数据
        response_data = {
            'category': category_serializer.data,
            'products': {
                'count': paginator.page.paginator.count,
                'next': paginator.get_next_link(),
                'previous': paginator.get_previous_link(),
                'results': product_serializer.data
            }
        }

        return Response(response_data)


# ################ 用微信头像和昵称登录 ####################
class LoginView(APIView):
    permission_classes = [AllowAny]  # 允许匿名访问
    """微信头像和昵称登录接口"""

    def post(self, request, *args, **kwargs):
        # 获取参数
        code = request.data.get('code')
        avatarUrl = request.data.get('avatarUrl')
        nickName = request.data.get('nickName')
        # 1. 获取session_key和openid
        wx_api = f"https://api.weixin.qq.com/sns/jscode2session?" \
                 f"appid={settings.WECHAT_APPID}&" \
                 f"secret={settings.APP_SECRET}&" \
                 f"js_code={code}&grant_type=authorization_code"

        response = requests.get(wx_api)
        wx_data = response.json()

        # 错误处理
        if 'errcode' in wx_data:
            return Response({
                'code': wx_data['errcode'],
                'msg': wx_data['errmsg']
            }, status=status.HTTP_400_BAD_REQUEST)

        # 3. 创建/更新用户
        user, created = UserInfo.objects.update_or_create(
            openid=wx_data['openid'],
            defaults={
                'nickName': nickName,
                'avatarUrl': avatarUrl,
                'session_key': wx_data['session_key'],
                'username': nickName  # 昵称作为用户名
            }
        )
        # 生成JWT token(原生,后续简单)
        refresh = RefreshToken.for_user(user)
        token = ({
            'access': str(refresh.access_token),
            'refresh': str(refresh)
        })

        return Response({
            'success': True,
            'token': token,
            'mobile': nickName  # 暂返回昵称代替手机号,以后需要再改
        })


# ################ 用手机号登录获取加密数据,手机号等 ####################
class WeChatLoginView(APIView):
    permission_classes = [AllowAny]  # 允许匿名访问
    """微信手机号登录接口"""

    def post(self, request):
        # 获取参数
        code = request.data.get('code')
        encrypted_data = request.data.get('encryptedData')
        iv = request.data.get('iv')

        # 1. 获取session_key和openid
        wx_api = f"https://api.weixin.qq.com/sns/jscode2session?" \
                 f"appid={settings.WECHAT_APPID}&" \
                 f"secret={settings.APP_SECRET}&" \
                 f"js_code={code}&grant_type=authorization_code"

        response = requests.get(wx_api)
        wx_data = response.json()

        # 错误处理
        if 'errcode' in wx_data:
            return Response({
                'code': wx_data['errcode'],
                'msg': wx_data['errmsg']
            }, status=status.HTTP_400_BAD_REQUEST)

        # 2. 解密手机号
        try:
            crypt = WXBizDataCrypt(settings.WECHAT_APPID, wx_data['session_key'])
            decrypted_data = crypt.decrypt(encrypted_data, iv)
            mobile = decrypted_data['purePhoneNumber']
        except Exception as e:
            return Response({
                'code': 40002,
                'msg': f'数据解密失败: {str(e)}'
            }, status=status.HTTP_400_BAD_REQUEST)

        # 3. 创建/更新用户
        user, created = UserInfo.objects.update_or_create(
            openid=wx_data['openid'],
            defaults={
                'phone': mobile,
                'session_key': wx_data['session_key'],
                'username': mobile  # 手机号作为用户名
            }
        )
        # user, _ = WeChatUser.objects.get_or_create(openid=wx_data['openid'])
        # 4. 生成JWT Token(手动,后续配置比较麻烦)
        # token = jwt.encode(
        #     {'user_id': user.id, 'mobile': mobile},
        #     settings.SECRET_KEY,
        #     algorithm='HS256'
        # )
        # 生成JWT token(原生,后续简单)
        refresh = RefreshToken.for_user(user)
        token = ({
            'access': str(refresh.access_token),
            'refresh': str(refresh)
        })

        return Response({
            'success': True,
            'token': token,
            'mobile': mobile
        })


# 获取和储存头像和昵称
class AvatNickNameAPIView(APIView):
    permission_classes = [IsAuthenticated]  # 验证用户是否有权限访问此接口
    parser_classes = [MultiPartParser, FormParser]  # 关键：允许文件解析，但前端请求头有所不同

    def get(self, request):
        user = request.user
        serializer = UserInfoSerializer(user)
        return Response(serializer.data)

    def post(self, request):
        user = request.user
        serializer = UserInfoSerializer(user, data=request.data, partial=True)  # partial=True允许部分更新
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class OrdersViewSet(ModelViewSet):  # 订单查询
    serializer_class = SalesOrderSerializer
    permission_classes = [IsAuthenticated]

    # 当使用ModelViewSet或GenericViewSet时，DRF要求必须明确指定数据源，需要以下两种方式之一：
    # 1.在视图集中定义: queryset
    # 2.属性重写: get_queryset()方法

    def get_queryset(self):
        from rest_framework.exceptions import ValidationError
        """ 限定只返回当前用户的订单，并支持状态过滤 """
        queryset = SalesOrder.objects.filter(user=self.request.user)
        # 添加状态过滤（示例：/orders/?status=paid）
        status = self.request.query_params.get('status')
        # print('status:', status)

        if status:
            if str(status) == 'all':
                # 特殊值处理(返回此用户的所有订单)
                pass
            else:
                if str(status) == 'afterSales':
                    queryset = queryset.filter(status__in=['refunding', 'refunded'])
                else:
                    # 验证状态有效性
                    valid_statuses = dict(SalesOrder.ORDER_STATUS_CHOICES).keys()
                    if status not in valid_statuses:
                        raise ValidationError(
                            f"无效状态参数，可选值：{list(valid_statuses)} 或 all",
                            code='invalid_status'
                        )
                    queryset = queryset.filter(status=status)

        return queryset.prefetch_related('items').order_by('-created_at')

    # 对高频访问的订单列表接口添加缓存：
    @method_decorator(cache_page(60 * 1))  # 缓存1分钟
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)

    def get_serializer_context(self):
        context = super().get_serializer_context()
        context['request'] = self.request  # 传递 request
        return context

    # 关键方案2：关闭不需要的HTTP方法（可选）
    http_method_names = ['get', 'head', 'options']  # 仅允许查询


# #############  物流信息跟踪  #################
class LogisticsTrackView(APIView):
    """ 物流信息查询接口 """
    # authentication_classes = [TokenAuthentication]  # 因使用的是JWT认证，在settings里已经全局设置了，你这个是原生的会存token到数据库，不能加进来，否则不会通过
    permission_classes = [IsAuthenticated]

    def get(self, request):
        tracking_no = request.query_params.get('tracking_no')  # 快递单号
        company_code = request.query_params.get('company_code')  # 快递公司code
        phone = request.query_params.get('phone')  # 收件人电话,顺丰要求
        customer_name = request.query_params.get('customerName')  # 收件人姓名,京东要求
        try:
            logistics = LogisticsInfo.objects.get(tracking_number=tracking_no)
            # print('到没用第三方：', logistics)
            serializer = LogisticsSerializer(logistics)
            return Response(serializer.data)
        except LogisticsInfo.DoesNotExist:
            # 调用快递100接口（快递100）
            data = self.query_kuaidi100(company_code, tracking_no, phone, customer_name)
            return Response(data)

    def query_kuaidi100(self, company_code, tracking_no, phone, customer_name):
        api_key = 'CpzgAqXA1596'  # 控制台获取:KEY
        customer = '21884869D20349FDD70E435FC006464D'  # 控制台获取:免费用户ID：CUSTOMER
        url = "https://poll.kuaidi100.com/poll/query.do"
        """返回统一结构：{code: 0, msg: 'ok', ...} 或 {code: 错误码, msg: 错误信息}"""
        try:
            # 参数校验
            if not company_code or not tracking_no:
                return {"code": 4001, "msg": "快递公司和单号不能为空"}
            # 1. 构造请求参数
            param = {
                "com": company_code.lower(),  # 快递公司编码必须小写
                "num": tracking_no,  # 单号（最长32字符）
                "resultv2": 1,  # 增强轨迹信息
                "phone": phone.split('-')[-1][-4:],  # 顺丰查询必须提供phone参数, 分机取后四位
                "customerName": customer_name,  # 京东查询必须提供收件人
                "country": ''  # 国际查询需国家代码:如CN
            }
            param_json = json.dumps(param, separators=(',', ':'))  # 紧凑JSON格式
            # 2. 生成签名:按 param + api_key + customer 顺序拼接（注意无+号）
            sign_str = param_json + api_key + customer
            md5 = hashlib.md5()
            md5.update(sign_str.encode('utf-8'))
            sign = md5.hexdigest().upper()  # 必须转大写
            # 3. 组装POST数据
            post_data = {
                "customer": customer,
                "sign": sign,
                "param": param_json
            }
            # 4. 发送请求（必须用POST）
            headers = {'Content-Type': 'application/x-www-form-urlencoded'}
            response = requests.post(url, data=post_data, headers=headers)
            # 5. 处理HTTP错误
            if response.status_code != 200:
                return {
                    "code": 5001,
                    "msg": f"快递100服务不可用（HTTP {response.status_code}）"
                }
            # 6. 处理响应
            result = response.json()
            if result.get('status') != '200':
                return {
                    "code": int(result.get('returnCode', 5000)),
                    "msg": result.get('message', '未知错误')
                }
            """解析成功响应"""
            return {
                "status": result['state'],  # 0-在途 1-揽收 2-疑难 3-签收
                "company": result['com'],  # 快递公司名称
                "tracking_no": result['nu'],  # 快递单号
                "traces": [
                    {
                        "time": item['ftime'],  # 格式化时间
                        "detail": item['context'],
                        "status": item['status'],
                        "areaCode": item['areaCode'],
                        "areaName": item['areaName']
                    } for item in result['data']
                ]
            }
        except requests.exceptions.Timeout:
            return {"code": 5002, "msg": "请求超时，请稍后重试"}
        except Exception as e:
            return {"code": 5000, "msg": f"系统错误：{str(e)}"}

    # 获取支持的快递公司列表
    def get_supported_carriers(self):
        url = "http://api.kuaidi100.com/carrier/queryCarrierList"
        response = requests.get(url)
        return {c['code'].lower(): c['name'] for c in response.json()['data']}

    # 对于DHL、UPS等国际快递，需补充以下参数：
    # params.update({
    #     "country": "US",  # 国家代码
    #     "postcode": "95035",
    #     "language": "en"  # 返回语言
    # })

